#include "String.hpp"
#include <Windows.h>

namespace ge {

//----------------------------------------------------------------------------//
// String::Buffer
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
String::Buffer::Buffer( void ) : refcnt( 1 ), length( 0 ), size( 1 ), data( new char[1] )
{
     data[0] = 0;    
}
//----------------------------------------------------------------------------//
String::Buffer::~Buffer( void )
{
     assert( refcnt == 0 );
     delete [] data;
}
//----------------------------------------------------------------------------//
String::Buffer::Buffer( const char* _str, uint _offset, int _length, int _reserve, bool _valid_params ) : refcnt( 1 ), data( 0 )
{
     if( !_valid_params )
     {
          uint _real_str_length = String::Length( _str );
          if( _length < 0 || _length > (int)_real_str_length ) _length = _real_str_length;
          if( _offset > _real_str_length ) _offset = _real_str_length;
          if( _offset + _length > _real_str_length ) _length = _real_str_length - _offset;
     }
     length = _length;
     size = geChoose( _reserve > (int)length+1, _reserve + 1, length + 1 ) + 1;
     data = new char[size+1];
     if( _str && _length ) memcpy( data, _str + _offset, length );
     data[ length ] = 0;
}
//----------------------------------------------------------------------------//
String::Buffer* String::Buffer::Clone( uint _offset, int _length, int _reserve, bool _valid_params ) const
{
     return new Buffer( data, _offset, _length, _reserve, _valid_params );    
}
//----------------------------------------------------------------------------//
String::Buffer* String::Buffer::Exchange( const Buffer* _buffer )
{
     assert( _buffer );

     _buffer -> AddRef();
     this -> Release();

     return const_cast< Buffer* >( _buffer );
}
//----------------------------------------------------------------------------//
String::Buffer* String::Buffer::OnModify( int _reserve )
{
     if( _reserve < 0 ) _reserve = size;

     if( refcnt > 1 || (uint)_reserve > size )
     {
          Buffer* _copy = Clone( 0, length, _reserve, true );
          Release();
          return _copy;
     }

     return this;
}
//----------------------------------------------------------------------------//
String::Buffer* String::Buffer::OnAppend( uint _append )
{
     if( refcnt > 1 || length + _append + 1 > size )
     {
          Buffer* _copy = Clone( 0, length, (length + _append + 1) << 1, true );
          Release();
          return _copy;
     }

     return this;
}
//----------------------------------------------------------------------------//
String::Buffer* String::Buffer::AddRef( void ) const
{
     ++ refcnt;
     return const_cast< Buffer* >( this );
}
//----------------------------------------------------------------------------//
String::Buffer* String::Buffer::Release( void )
{
     assert( refcnt > 0 );

     if( !( -- refcnt ) )
     {
          delete this;
          return 0;
     }

     return this;
}
//----------------------------------------------------------------------------//
String::Buffer* String::Buffer::_GetDefaultBuffer( void )
{
     static Buffer* _buffer = new Buffer();
     return _buffer;
}
//----------------------------------------------------------------------------//
String::Buffer* String::Buffer::_FromChar( char _ch )
{
     char _str[] = { _ch, 0 };
     return new Buffer( _str );
}
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
// String
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
String::String( void ) : m_buffer( Buffer::_GetDefaultBuffer() -> AddRef() )
{
}
//----------------------------------------------------------------------------//
String::~String( void )
{
     m_buffer -> Release();
}
//----------------------------------------------------------------------------//
String::String( int _ch ) : m_buffer( geChoose( _ch, Buffer::_FromChar( _ch ), Buffer::_GetDefaultBuffer() -> AddRef() ) )
{
}
//----------------------------------------------------------------------------//
String::String( char _ch ) : m_buffer( geChoose( _ch, Buffer::_FromChar( _ch ), Buffer::_GetDefaultBuffer() -> AddRef() ) ) 
{
}
//----------------------------------------------------------------------------//
String::String( const char* _str ) : m_buffer( geChoose( _str, new Buffer( _str ), Buffer::_GetDefaultBuffer() -> AddRef() ) )
{
}
//----------------------------------------------------------------------------//
String::String( const char* _str, uint _offset, int _length ) : m_buffer( geChoose( _str, new Buffer( _str, _offset, _length ), Buffer::_GetDefaultBuffer() -> AddRef() ) )
{
}
//----------------------------------------------------------------------------//
String::String( const String& _str ) : m_buffer( _str.m_buffer -> AddRef() )
{
}
//----------------------------------------------------------------------------//
String::String( const String& _str, uint _offset, int _length ) : m_buffer( _str.m_buffer -> Clone( _offset, _length ) )
{
}
//----------------------------------------------------------------------------//
String::String( const Buffer* _buffer ) : m_buffer( geChoose( _buffer, _buffer -> AddRef(), Buffer::_GetDefaultBuffer() -> AddRef() ) )
{
}
//----------------------------------------------------------------------------//
String& String::Append( char _ch )
{
     if( _ch )
     {
          m_buffer = m_buffer -> OnAppend( 1 );
          m_buffer -> data[ m_buffer -> length ] = _ch;
          m_buffer -> length ++;
          m_buffer -> data[ m_buffer -> length ] = 0;
     }

     return *this;
}
//----------------------------------------------------------------------------//
String& String::Append( char _ch, uint _count )
{
     if( _ch && _count )
     {
          m_buffer = m_buffer -> OnAppend( _count );
          while( _count -- )
          {
               m_buffer -> data[ m_buffer -> length ] = _ch;
               m_buffer -> length ++;                
          }
          m_buffer -> data[ m_buffer -> length ] = 0;
     }

     return *this;
}
//----------------------------------------------------------------------------//
String& String::Append( const char* _str, uint _offset, int _length )
{
     if( IsInRange( _str, m_buffer -> data, m_buffer -> Size() ) )
     {
          struct _tmp_buffer
          {
               Buffer* _buffer;
               _tmp_buffer( void ) : _buffer( 0 ) {}
               ~_tmp_buffer( void ){ if( _buffer )_buffer -> Release(); }
          };
          _tmp_buffer _tmp;
          _tmp._buffer = new Buffer( _str, _offset, _length );
          Append( _tmp._buffer -> data, _length );
          return *this;
     }

     uint _real_str_length = Length( _str );
     if( _length < 0 || _length > (int)_real_str_length ) _length = (int)_real_str_length;
     if( _offset > _real_str_length ) _offset = _real_str_length;
     if( _offset + _length > _real_str_length ) _length = _real_str_length - _offset;

     if( _length && _str )
     {
          m_buffer = m_buffer -> OnAppend( _length );
          memcpy( m_buffer -> data + m_buffer -> length, _str, _length ); 
          m_buffer -> length += _length;
          m_buffer -> data[ m_buffer -> length ] = 0;
     }

     return *this;
}
//----------------------------------------------------------------------------//
uint String::Length( const char* _str )
{
     return geChoose( _str, strlen( _str ), 0 );
}
//----------------------------------------------------------------------------//
int String::Compare( const char* _a, const char* _b )
{
     return geChoose( _a && _b, strcmp( _a, _b ), _a - _b );
}
//----------------------------------------------------------------------------//
int String::CompareIgnoreCase( const char* _a, const char* _b )
{
     return geChoose( _a && _b, lstrcmpiA( _a, _b ), _a - _b );
}
//----------------------------------------------------------------------------//
String String::FormatVL( const char* _format, va_list _args )
{
 //this code from ptypes: pputf.cxx

     struct
     {
          bool operator()( char _ch )
          {
               return ( _ch == ' ' ) ||
                      ( _ch == '#' ) ||
                      ( _ch == '+' ) ||
                      ( _ch == '~' ) ||
                      ( _ch == '-' ) ||
                      ( _ch == '.' ) ||
                      ( _ch >= '0' && _ch <= '9' );
          }
         
     } _check_fmtopts;
     

     enum fmt_type_t
     {
         FMT_NONE,
         FMT_CHAR,
         FMT_SHORT,
         FMT_INT,
         FMT_LONG,
         FMT_LARGE,
         FMT_STR,
         FMT_PTR,
         FMT_DOUBLE,
         FMT_LONG_DOUBLE,
         FMT_IPADDR,
         FMT_TIME,
         FMT_LONGTIME
     };

    String _res;
    const char* p = _format;
    while ( p && *p != 0)
    {
        // write out raw data between format specifiers
        const char* e = strchr(p, '%');
        if (e == 0)
            e = p + strlen(p);
        if (e > p)
            _res.Append(p, 0, e - p);

        if (*e != '%')
            break;

        e++;
        if (*e == '%')
        {
            // write out a single '%'
            _res.Append('%');
            p = e + 1;
            continue;
        }

        // build a temporary buffer for the conversion specification
        char fbuf[128];
        fbuf[0] = '%';
        char* f = fbuf + 1;
        bool modif = false;

        // formatting flags and width specifiers
        while ( _check_fmtopts(*e) && uint(f - fbuf) < sizeof(fbuf) - 5)
        {
            *f++ = *e++;
            modif = true;
        }

        // prefixes
        fmt_type_t fmt_type = FMT_NONE;
        switch(*e)
        {
        case 'h': 
            fmt_type = FMT_SHORT; 
            *f++ = *e++;
            break;
        case 'L': 
            fmt_type = FMT_LONG_DOUBLE; 
            *f++ = *e++;
            break;
        case 'l':
            e++;
            if (*e == 'l')
            {
#if defined(_MSC_VER) || defined(__BORLANDC__)
                *f++ = 'I';
                *f++ = '6';
                *f++ = '4';
#else
                *f++ = 'l';
                *f++ = 'l';
#endif
                e++;
                fmt_type = FMT_LARGE;
            }
            else
            {
                *f++ = 'l';
                fmt_type = FMT_LONG;
            }
            break;
        }

        // format specifier
        switch(*e)
        {
        case 'c':
            fmt_type = FMT_CHAR;
            *f++ = *e++;
            break;
        case 'd':
        case 'i':
        case 'o':
        case 'u':
        case 'x':
        case 'X':
            if (fmt_type < FMT_SHORT || fmt_type > FMT_LARGE)
                fmt_type = FMT_INT;
            *f++ = *e++;
            break;
        case 'e':
        case 'E':
        case 'f':
        case 'g':
        case 'G':
            if (fmt_type != FMT_LONG_DOUBLE)
                fmt_type = FMT_DOUBLE;
            *f++ = *e++;
            break;
        case 's':
            fmt_type = FMT_STR;
            *f++ = *e++;
            break;
        case 'p':
            fmt_type = FMT_PTR;
            *f++ = *e++;
            break;
        case 'a':
            fmt_type = FMT_IPADDR;
            *f++ = *e++;
            break;
        case 't':
            fmt_type = FMT_TIME;
            *f++ = *e++;
            break;
        case 'T':
            fmt_type = FMT_LONGTIME;
            *f++ = *e++;
            break;
        }

        if (fmt_type == FMT_NONE)
            break;

        *f = 0;

        // some formatters are processed here 'manually',
        // while others are passed to snprintf
        char buf[4096];
        int s = 0;
        switch(fmt_type)
        {
        case FMT_NONE: 
            break; // to avoid compiler warning
        case FMT_CHAR:
            if (modif)
                s = _snprintf(buf, sizeof(buf), fbuf, va_arg(_args,int)); 
            else
                _res.Append(char(va_arg(_args,int)));
            break;         
        case FMT_SHORT:
            s = _snprintf(buf, sizeof(buf), fbuf, va_arg(_args,int)); 
            break;
        case FMT_INT:
            s = _snprintf(buf, sizeof(buf), fbuf, va_arg(_args,int)); 
            break;
        case FMT_LONG:
            s = _snprintf(buf, sizeof(buf), fbuf, va_arg(_args,long)); 
            break;
        case FMT_LARGE:
            s = _snprintf(buf, sizeof(buf), fbuf, va_arg(_args,int64)); 
            break;
        case FMT_STR:
            if (modif)
                s = _snprintf(buf, sizeof(buf), fbuf, va_arg(_args,char*));
            else
                _res.Append(va_arg(_args,const char*));
            break;
        case FMT_PTR:
            s = _snprintf(buf, sizeof(buf), fbuf, va_arg(_args,void*)); 
            break;
        case FMT_DOUBLE: 
        {
             s = _snprintf(buf, sizeof(buf), fbuf, va_arg(_args,double));
             for( ; s > 0; -- s )
             {
                  if( buf[s-1] == '0' ) buf[s-1] = 0;
                  else if( buf[s-1] == '.' ) { buf[s-1] = 0; break; }
                  else { break; }
             }
        } break;
        case FMT_LONG_DOUBLE:
            s = _snprintf(buf, sizeof(buf), fbuf, va_arg(_args,long double)); 
            break;

        case FMT_IPADDR:
            {
                union { long l; uint8 b[4]; } ip = { va_arg(_args,long) };
                s = _snprintf(buf, sizeof(buf), "%d.%d.%d.%d", 
                    uint(ip.b[0]), uint(ip.b[1]), uint(ip.b[2]), uint(ip.b[3]));
            }
            break;
        
        //case FMT_TIME:
        //case FMT_LONGTIME:
        //    {
        //        char* fmt = fmt_type == FMT_TIME ? shorttimefmt : longtimefmt;
        //        struct tm t;
        //        int64 dt = va_arg(va,int64);
        //        if (dt < 0)
        //            dt = 0;
        //        s = strftime(buf, sizeof(buf), fmt, dttotm(dt, t));
        //    }
        //    break;
        }
        if (s > 0)
            _res.Append(buf, 0, s);
        
        p = e;
    }
                
    return _res;
}
//----------------------------------------------------------------------------//
String String::Format( const char* _format, ... )
{
     va_list _args;
     va_start( _args, _format );
     //return FormatVL( _format, (char *)&_format + sizeof( &_format ) );
     String _str = FormatVL( _format, _args );
     va_end( _args );
     return _str;
}
//----------------------------------------------------------------------------//
char* String::SizeAsString( char* _buffer, uint _max_length, uint _value )
{
     if( !_buffer || !_max_length ) return 0;

     uint _size = _value;

     if( _size >= 900 )
     {
          _size = _size / 1024;
             
          if( _size >= 900 )
          {
               _size = _size / 1024;
                                                             
               if( _size >= 900 ) _snprintf( _buffer, _max_length, "%.2f gb (%u b)", float( _value ) / 1073741824.f, _value );
               else _snprintf( _buffer, _max_length, "%.2f mb (%u b)", float( _value ) / 1048576.f, _value );
          }
          else _snprintf( _buffer, _max_length, "%.2f kb (%u b)", float( _value ) / 1024.f, _value );
     }
     else _snprintf( _buffer, _max_length, "%u b", _value );

     return _buffer;
}
//----------------------------------------------------------------------------//
String String::SizeAsString( uint _value )
{
     char _buff[256];
     return SizeAsString( _buff, 254, _value );
}
//----------------------------------------------------------------------------//
const char* String::Search( const char* _src, const char* _str )
{
     return geChoose( _src && _str, strstr( const_cast< char* >( _src ), _str ), 0 );
}
//----------------------------------------------------------------------------//
const char* String::Search( const char* _src, char _ch )
{
     char _str[] = { _ch, 0 };
     return Search( _src, _str );
}
//----------------------------------------------------------------------------//
const char* String::SearchLast( const char* _src, const char* _str )
{
     if( _src && _str )
     {
          const char* _last = 0;
          char* _tmp = const_cast< char* >( _src );
          do
          {
               _tmp = strstr( _tmp, _str );
               if( _tmp ) _last = _tmp;

          } while( _tmp != 0 );

          return _last;
     }

     return 0;
}
//----------------------------------------------------------------------------//
const char* String::SearchLast( const char* _src, char _ch )
{
     char _str[] = { _ch, 0 };
     return SearchLast( _src, _str );
}
//----------------------------------------------------------------------------//
char* String::Search( char* _src, const char* _str )
{
     return geChoose( _src && _str, strstr( _src, _str ), 0 );
}
//----------------------------------------------------------------------------//
char* String::Search( char* _src, char _ch )
{
     char _str[] = { _ch, 0 };
     return Search( _src, _str );
}
//----------------------------------------------------------------------------//
char* String::SearchLast( char* _src, const char* _str )
{
     if( _src && _str )
     {
          char* _last = 0;
          char* _tmp = _src;
          do
          {
               _tmp = strstr( _tmp, _str );
               if( _tmp ) _last = _tmp;

          } while( _tmp != 0 );

          return _last;
     }

     return 0;
}
//----------------------------------------------------------------------------//
char* String::SearchLast( char* _src, char _ch )
{
     char _str[] = { _ch, 0 };
     return SearchLast( _src, _str );
}
//----------------------------------------------------------------------------//
bool String::ToBool( const char* _value, bool _default )
{
     if( _value )                      
     {
          if( !_stricmp( _value, "true" ) || !_stricmp( _value, "yes" ) ) return true;

          bool _float = false;
          for( const char* ch = _value; *ch; ++ ch ) { if( *ch == '.' ) { _float = true; break; } }

          if( _float )
          {
               float _f;
               if( sscanf( _value, "%f", &_f ) ) return _f != 0.0f;
          }
          else
          {
               bool _hex = false;
               for( const char* ch = _value; *ch; ++ ch ) { if( *ch == 'x' || *ch == 'X' ) { _hex = true; break; } }
               int _i;
               if( _hex && sscanf( _value, "0x%x", &_i ) ) return _i != 0; 
               if( sscanf( _value, "%d", &_i ) ) return _i != 0;
          }
     }

     return _default;
}
//----------------------------------------------------------------------------//
int String::ToInt( const char* _value, int _default )
{
     if( _value )
     {
          bool _float = false;
          for( const char* ch = _value; *ch; ++ ch ) { if( *ch == '.' ) { _float = true; break; } }

          if( _float )
          {
               float _f;
               if( sscanf( _value, "%f", &_f ) ) return (int)_f;
          }
          else
          {
               bool _hex = false;
               for( const char* ch = _value; *ch; ++ ch ) { if( *ch == 'x' || *ch == 'X' ) { _hex = true; break; } }
               int _i;
               if( _hex && sscanf( _value, "0x%x", &_i ) ) return _i; 
               if( sscanf( _value, "%d", &_i ) ) return _i;
          }

          if( !_stricmp( _value, "true" ) || !_stricmp( _value, "yes" ) ) return 1;
     }

     return _default;
}
//----------------------------------------------------------------------------//
float String::ToFloat( const char* _value, float _default )
{
     if( _value )
     {
          bool _float = false;
          for( const char* ch = _value; *ch; ++ ch ) { if( *ch == '.' ) { _float = true; break; } }

          if( _float )
          {
               float _f;
               if( sscanf( _value, "%f", &_f ) ) return _f;
          }
          else
          {
               bool _hex = false;
               for( const char* ch = _value; *ch; ++ ch ) { if( *ch == 'x' || *ch == 'X' ) { _hex = true; break; } }
               int _i;
               if( _hex && sscanf( _value, "0x%x", &_i ) ) return (float)_i; 
               if( sscanf( _value, "%d", &_i ) ) return (float)_i;
          }

          if( !_stricmp( _value, "true" ) || !_stricmp( _value, "yes" ) ) return 1.0f;
     }

     return _default;
}
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
//
//----------------------------------------------------------------------------//

} // namespace ge
